package com.jrx.anytxn.transaction.service.impl;

import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.function.ToBigDecimalFunction;
import com.jrx.anytxn.common.function.UncheckedFunction;
import com.jrx.anytxn.common.uitl.BeanUtils;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.param.dto.product.InstallmentFeeTableRes;
import com.jrx.anytxn.param.dto.product.PrPrepaymentFeeRes;
import com.jrx.anytxn.param.dto.product.PrPreterminationFeeTableRes;
import com.jrx.anytxn.param.service.product.IInstallmentFeeTableService;
import com.jrx.anytxn.param.service.product.IPrPrepaymentFeeService;
import com.jrx.anytxn.param.service.product.IPrPreterminationFeeTableService;
import com.jrx.anytxn.transaction.bean.*;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.entity.AmLoanAccountInfo;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.service.IAccountService;
import com.jrx.anytxn.transaction.service.IFeeService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * @program: anytxn-cf-transaction
 * @description: 费用处理操作类f
 * @author: xiaoyu.jing
 * @date: 2019-10-16 13:22
 **/
@Service
public class FeeServiceImpl implements IFeeService {

    private static final Logger logger = LoggerFactory.getLogger(FeeServiceImpl.class);

    @Autowired
    private IPrPreterminationFeeTableService prPreterminationFeeTableService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IInstallmentFeeTableService iInstallmentFeeTableService;
    @Autowired
    private IPrPrepaymentFeeService prPrepaymentFeeService;

    /**
     * 计算提前结清手续费,获取提前结清费用交易账户,分期费用交易账户，利息交易账户
     *
     * @param saveBean
     * @param loanMainInfo
     * @param bussinessDate
     * @throws Exception
     */
    @Override
    public void castPayOffFeeAndIntrAcct(PaymentSaveBean saveBean, AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,
                                         DelqParamBean delqParamBean, Date bussinessDate) throws TxnException {
        logger.info("计算提前结清手续费,获取交易账户,loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));

        //获取提前结清费用参数
        PrPreterminationFeeTableRes res = prPreterminationFeeTableService.findByPreterminationFeeId(loanMainInfo.getPayoffTableId(),
                loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        logger.info("提前结清费用参数,PrPreterminationFeeTableRes:{}", BeanUtils.toMap(res));

        if(null != res.getWaiveFeeTenor() && res.getWaiveFeeTenor() <= loanAccountInfo.getPayedTenor()){
            return;
        }

        //未抛帐的还款计划
        //筛选未抛帐
        List<AmRepaymentPlanInfo> planInfoList = saveBean.getPlanInfoMap().values().stream()
                .filter(amRepaymentPlanInfo -> TransBizConstant.LOAN_PLAN_STATUS_0.equals(amRepaymentPlanInfo.getPlanStatus()))//获取还款计划
                .collect(Collectors.toList());

        String liabilityFlag = saveBean.getPlanInfoMap().values().stream().findFirst().get().getLiabilityFlag();

        //生成提前结清费用交易账户
        BigDecimal remainPrincipal = loanAccountInfo.getRemainPrincipal();
        BigDecimal payOffFee = this.calculationPayOffFee(res,remainPrincipal);
        if (payOffFee.compareTo(BigDecimal.ZERO) > 0) {
            //如果提前结清手续费大于0，则生成相应的费用交易账户
            CastAccountTransactionBean castPaymentFeeAccount = accountService.castPaymentFeeAccount(loanMainInfo,loanAccountInfo,
                    TransBizConstant.TXN_CODE_INST_PAYOFF_FEE, payOffFee, bussinessDate,liabilityFlag);
            saveBean.addSaveAcct(castPaymentFeeAccount.getAmAccountMainInfo());
            saveBean.addTransactionInfo(castPaymentFeeAccount.getTlTransactionInfo());
            saveBean.addSaveGlList(castPaymentFeeAccount.getTlGlInterfaceInfoList());
            logger.info("提前结清费用交易账户,castPaymentFeeAccount:{}", BeanUtils.toMap(castPaymentFeeAccount));
        }

        //获取分期费用账户
        List<CastAccountTransactionBean> castInstallmentFeeAcctList = this.castInstallmentFeeAcct(res, loanMainInfo,
                loanAccountInfo,
                planInfoList, bussinessDate);
        castInstallmentFeeAcctList.forEach(castAccountTransactionBean -> {
            saveBean.addSaveAcct(castAccountTransactionBean.getAmAccountMainInfo());
            saveBean.addTransactionInfo(castAccountTransactionBean.getTlTransactionInfo());
            saveBean.addSaveGlList(castAccountTransactionBean.getTlGlInterfaceInfoList());
        });


        //获取未抛期数利息交易账户
        List<CastAccountTransactionBean> castAccountTransactionBeanList = this.castIntrAcct(res, loanMainInfo, loanAccountInfo, planInfoList,
                delqParamBean,bussinessDate);
        castAccountTransactionBeanList.forEach(castAccountTransactionBean -> {
            saveBean.addSaveAcct(castAccountTransactionBean.getAmAccountMainInfo());
            saveBean.addTransactionInfo(castAccountTransactionBean.getTlTransactionInfo());
            saveBean.addSaveGlList(castAccountTransactionBean.getTlGlInterfaceInfoList());
        });
    }

    /**
     * 计算提前结清罚费
     *
     * @param res
     * @param remainPrincipal
     * @return
     */
    private BigDecimal calculationPayOffFee(PrPreterminationFeeTableRes res, BigDecimal remainPrincipal) {
        BigDecimal feeAmt = BigDecimal.ZERO;
        if (TransBizConstant.ADVANCE_SETTLE_FEE_CHARGE_OPTION_0.equals(res.getFeeChargeOption())) {
            //按比例
            feeAmt = remainPrincipal.multiply(res.getFeeChargePercent()).setScale(TransBizConstant.BIGDECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
        } else if (TransBizConstant.ADVANCE_SETTLE_FEE_CHARGE_OPTION_1.equals(res.getFeeChargeOption())) {
            //按固定金额
            feeAmt = res.getFeeChargeAmount();
        }

        //比较最大金额
        if (null !=res.getMaxAmount() && feeAmt.compareTo(res.getMaxAmount()) > 0) {
            feeAmt = res.getMaxAmount();
        }

        //比较最小金额
        if (null !=res.getMinAmount() && feeAmt.compareTo(res.getMinAmount()) < 0) {
            feeAmt = res.getMinAmount();
        }
        return feeAmt;
    }

    /**
     * 通过订单，还款计划抛出分期费用交易账户
     *
     * @param res
     * @param loanMainInfo
     * @param planInfoList
     * @param bussinessDate
     * @return
     */
    private List<CastAccountTransactionBean> castInstallmentFeeAcct(PrPreterminationFeeTableRes res, AmLoanMainInfo loanMainInfo,
                                                                    AmLoanAccountInfo amLoanAccountInfo,
                                                           List<AmRepaymentPlanInfo> planInfoList, Date bussinessDate) {
        List<CastAccountTransactionBean> castAccountTransactionBeanList = new ArrayList<>();
        String feeMethod = res.getFeeMethod();//费用收取方式
        if (StringUtils.isNotBlank(feeMethod)) {
            UncheckedFunction<AmRepaymentPlanInfo, CastAccountTransactionBean> getInstallmentFee = plan -> {
                //生成费用交易账户
                CastAccountTransactionBean castInstallmentFeeAccount = accountService.castInstallmentFeeAccount(loanMainInfo,
                        amLoanAccountInfo, plan, bussinessDate);
                logger.info("分期费用交易账户 castInstallmentFeeAccount:{}", BeanUtils.toMap(castInstallmentFeeAccount));
                return castInstallmentFeeAccount;
            };

            if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_1.equals(feeMethod)) {//剩余期数都收费
                logger.info("所有剩余期数抛出费用交易账户");
                castAccountTransactionBeanList = planInfoList.stream()
                        .map(CollectorsUtils.tranToUncheck(getInstallmentFee)) //生成交易账户
                        .filter(Objects::nonNull)//筛选生成交易账户不为空
                        .collect(Collectors.toList());
                return castAccountTransactionBeanList;
            } else if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_2.equals(feeMethod)) {//剩余X期收费
                int feeTenor = res.getFeeTenor();//收取费用期数
                logger.info("剩余{}期抛出费用交易账户", feeTenor);
                castAccountTransactionBeanList = planInfoList.stream()
                        .filter(plan -> plan.getBillingTenor() <= feeTenor + amLoanAccountInfo.getCastTenor())//筛选符合期数范围
                        .map(CollectorsUtils.tranToUncheck(getInstallmentFee))  //生成交易账户
                        .filter(Objects::nonNull)//筛选生成交易账户不为空
                        .collect(Collectors.toList());
            }
        }
        return castAccountTransactionBeanList;
    }

    /**
     * 通过订单，还款计划抛出利息交易账户
     *
     * @param res
     * @param loanMainInfo
     * @param planInfoList
     * @param bussinessDate
     * @return
     */
    private List<CastAccountTransactionBean> castIntrAcct(PrPreterminationFeeTableRes res, AmLoanMainInfo loanMainInfo,
                                                          AmLoanAccountInfo loanAccountInfo, List<AmRepaymentPlanInfo> planInfoList,
                                                          DelqParamBean delqParamBean, Date bussinessDate) {
        List<CastAccountTransactionBean> castAccountTransactionBeans = new ArrayList<>();
        String intMethod = res.getIntMethod();
        if (StringUtils.isNotBlank(intMethod)) {//利息收取方式
            UncheckedFunction<AmRepaymentPlanInfo, CastAccountTransactionBean> getIntrAcct = plan -> {
                //生成利息交易账户
                CastAccountTransactionBean castIntrAccount = accountService.castIntrAccount(loanMainInfo, loanAccountInfo,plan,
                        delqParamBean,bussinessDate);//生成利息交易账户
                logger.info("利息交易账户 castIntrAccount:{}", BeanUtils.toMap(castIntrAccount));
                return castIntrAccount;
            };

            if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_1.equals(intMethod)) {//剩余期数都收利息
                logger.info("所有剩余期数抛出利息交易账户");
                castAccountTransactionBeans = planInfoList.stream()
                        .map(CollectorsUtils.tranToUncheck(getIntrAcct))//生成交易账户
                        .filter(Objects::nonNull)//筛选生成交易账户不为空
                        .collect(Collectors.toList());
            } else if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_2.equals(intMethod)) {
                int intTenor = res.getIntTenor();
                logger.info("剩余{}期抛出利息交易账户", intTenor);
                castAccountTransactionBeans = planInfoList.stream()
                        .filter(plan -> plan.getBillingTenor() <= intTenor + loanAccountInfo.getCastTenor())//筛选符合期数范围
                        .map(CollectorsUtils.tranToUncheck(getIntrAcct))//生成交易账户
                        .filter(Objects::nonNull)//筛选生成交易账户不为空
                        .collect(Collectors.toList());
            }
        }
        return castAccountTransactionBeans;
    }

    /**
     * 计算提前还款手续费
     *
     * @param loanMainInfo
     * @return
     * @throws TxnBizException
     */
    @Override
    public BigDecimal getPrePayFee(AmLoanMainInfo loanMainInfo,AmLoanAccountInfo loanAccountInfo) throws TxnException {
        logger.info("计算提前还款费用，获取费用交易账户,loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
        PrPrepaymentFeeRes res = prPrepaymentFeeService.findByPrepaymentFeeId(
                loanMainInfo.getPrepayTableId(),loanMainInfo.getOrganizationId(),loanMainInfo.getTenantId());
        logger.info("计算提前还款费用，获取费用交易账户,res:{}", BeanUtils.toMap(res));
        BigDecimal prePayFee = this.calculationPrePayFee(res,loanAccountInfo);
        if (prePayFee.compareTo(BigDecimal.ZERO) <= 0) {
            logger.info("无费用");
            return BigDecimal.ZERO;
        }
        return prePayFee;
    }

    /**
     * 计算提前还款手续费，获取费用交易账户
     * @param loanMainInfo
     * @param loanAccountInfo
     * @param bussinessDate
     * @param liabilityFlag
     * @return
     * @throws TxnException
     */
    @Override
    public CastAccountTransactionBean getPrePayFeeAcct(AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo,Date bussinessDate,String liabilityFlag) throws TxnException {
        logger.info("计算提前还款费用，获取费用交易账户,loanMainInfo:{}", BeanUtils.toMap(loanMainInfo));
        PrPrepaymentFeeRes res = prPrepaymentFeeService.findByPrepaymentFeeId(
                loanMainInfo.getPrepayTableId(),loanMainInfo.getOrganizationId(),loanMainInfo.getTenantId());
        logger.info("计算提前还款费用，获取费用交易账户,res:{}", BeanUtils.toMap(res));
        BigDecimal prePayFee = this.calculationPrePayFee(res,loanAccountInfo);
        if (prePayFee.compareTo(BigDecimal.ZERO) <= 0) {
            logger.info("无费用");
            return null;
        }
        CastAccountTransactionBean castPaymentFeeAccount = accountService.castPaymentFeeAccount(loanMainInfo,loanAccountInfo,
                TransBizConstant.TXN_CODE_INST_PREPAY_FEE, prePayFee, bussinessDate,liabilityFlag);
        logger.info("计算提前还款费用，获取费用交易账户,castPaymentFeeAccount:{}", BeanUtils.toMap(castPaymentFeeAccount));
        return castPaymentFeeAccount;
    }

    /**
     * 计算提前还款罚费
     * @param res
     * @param loanAccountInfo
     * @return
     */
    private BigDecimal calculationPrePayFee(PrPrepaymentFeeRes res,AmLoanAccountInfo loanAccountInfo) {
        if(null != res.getWaiveFeeTenor() && res.getWaiveFeeTenor() <= loanAccountInfo.getPayedTenor()){
            return BigDecimal.ZERO;
        }
        return res.getFeeChargeAmount();
    }

    /**
     * 计算提前结清产生的手续费，分期费用以及利息
     *
     * @param loanMainInfo
     * @param planInfoList
     * @return
     * @throws TxnBizException
     */
    @Override
    public CalPayOffFeeBean getPayOffFeeAcct(AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, List<AmRepaymentPlanInfo> planInfoList) throws TxnBizException {
        logger.info("计算提前结清产生的手续费，分期费用以及利息,loanMainInfo:{},loanAccountInfo:{}", BeanUtils.toMap(loanMainInfo), BeanUtils.toMap(loanAccountInfo));
        CalPayOffFeeBean bean = new CalPayOffFeeBean();

        //获取提前结清费用参数
        String payOffTableId = loanMainInfo.getPayoffTableId();
        PrPreterminationFeeTableRes res = prPreterminationFeeTableService.findByPreterminationFeeId(payOffTableId,
                loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        logger.info("提前结清费用参数,PrPreterminationFeeTableRes:{}", BeanUtils.toMap(res));

        if(null != res.getWaiveFeeTenor() && res.getWaiveFeeTenor() <= loanAccountInfo.getPayedTenor()){
            return null;
        }

        //获取提前结清费用
        BigDecimal remainPrincipal = loanAccountInfo.getRemainPrincipal();
        BigDecimal payOffFee = this.calculationPayOffFee(res,remainPrincipal);
        logger.info("提前结清费用,payOffFee:{}", payOffFee);
        bean.setPayOffFeeAmt(payOffFee);

        int castTenor = loanAccountInfo.getCastTenor();
        //获取分期还款费用
        CalPayOffInstallmentFeeSubBean feeSubBean = this.getInstallmentFee(res, castTenor, planInfoList);
        logger.info("分期还款费用,feeSubBean:{}", BeanUtils.toMap(feeSubBean));
        bean.setInstallmentFeeSubBean(feeSubBean);

        //获取利息费用
        CalPayOffIntrSubBean intrSubBean = this.getIntrAmt(res, castTenor, planInfoList);
        logger.info("利息,intrSubBean:{}", BeanUtils.toMap(intrSubBean));
        bean.setIntrSubBean(intrSubBean);
        return bean;
    }

    /**
     * 获取分期还款费用
     *
     * @param res
     * @param castTenor
     * @param planInfoList
     * @return
     */
    private CalPayOffInstallmentFeeSubBean getInstallmentFee(PrPreterminationFeeTableRes res, int castTenor, List<AmRepaymentPlanInfo> planInfoList) {
        CalPayOffInstallmentFeeSubBean bean = new CalPayOffInstallmentFeeSubBean();
        String feeMethod = res.getFeeMethod();
        Map<Integer, BigDecimal> installmentFeeMap = new HashMap<>();
        if (StringUtils.isNotBlank(feeMethod)) {//费用收取方式
            Predicate<AmRepaymentPlanInfo> getUnCastPlan = plan -> TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus());
            ToBigDecimalFunction<AmRepaymentPlanInfo> getFeeAmt = plan -> plan.getFeeAmount().subtract(plan.getRepayFee());//计算应收分期费用
            if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_1.equals(feeMethod)) {//剩下期数都收
                logger.info("所有剩余期数计算分期费用");
                installmentFeeMap = planInfoList.stream()
                        .filter(getUnCastPlan)//筛选未抛帐
                        .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, getFeeAmt));
            } else if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_2.equals(feeMethod)) {//剩下X期收取
                int feeTenor = res.getFeeTenor();
                logger.info("剩余{}期计算分期费用", feeTenor);
                installmentFeeMap = planInfoList.stream()
                        .filter(getUnCastPlan)//筛选未抛帐
                        .filter(plan -> plan.getBillingTenor() <= feeTenor + castTenor)//筛选符合期数
                        .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, getFeeAmt));
            }
        }
        BigDecimal totalAmt = installmentFeeMap.values().stream().reduce(BigDecimal.ZERO,
                BigDecimal::add);//计算总分期费用
        bean.setTotalFeeAmt(totalAmt);
        bean.setInstallmentFeeAmtMap(installmentFeeMap);
        return bean;
    }

    /**
     * 获取利息费用
     *
     * @param res
     * @param castTenor
     * @param planInfoList
     * @return
     */
    private CalPayOffIntrSubBean getIntrAmt(PrPreterminationFeeTableRes res, int castTenor, List<AmRepaymentPlanInfo> planInfoList) {
        CalPayOffIntrSubBean bean = new CalPayOffIntrSubBean();
        String intMethod = res.getIntMethod();
        Map<Integer, BigDecimal> intrAmtMap = new HashMap<>();
        if (StringUtils.isNotBlank(intMethod)) {//利息收取方式
            Predicate<AmRepaymentPlanInfo> getUnCastPlan = plan -> TransBizConstant.LOAN_PLAN_STATUS_0.equals(plan.getPlanStatus());
            ToBigDecimalFunction<AmRepaymentPlanInfo> getIntAmt = plan -> plan.getInterestAmount().subtract(plan.getRepayInterest());//计算应收利息
            if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_1.equals(intMethod)) {//剩下期数都收
                logger.info("所有剩余期数计算利息");
                intrAmtMap = planInfoList.stream()
                        .filter(getUnCastPlan)//筛选未抛帐
                        .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, getIntAmt));
            } else if (TransBizConstant.ADVANCE_SETTLE_FEE_METHOD_2.equals(intMethod)) {//剩下X期收取
                int intTenor = res.getIntTenor();
                logger.info("剩余{}期计算利息", intTenor);
                intrAmtMap = planInfoList.stream()
                        .filter(getUnCastPlan)//筛选未抛帐
                        .filter(plan -> plan.getBillingTenor() <= intTenor + castTenor)
                        .collect(Collectors.toMap(AmRepaymentPlanInfo::getBillingTenor, getIntAmt));//筛选符合期数
            }
        }
        BigDecimal totalAmt = intrAmtMap.values().stream().reduce(BigDecimal.ZERO, BigDecimal::add);//计算总利息
        bean.setIntrAmtMap(intrAmtMap);
        bean.setTotalIntrAmt(totalAmt);
        return bean;
    }
}
